package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/blinkbean/dingtalk"
	"github.com/pkg/errors"
	"gopkg.in/yaml.v3"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

// 定义一个 Config 结构体变量
var config Config

func main() {
	ReadConfig()

	// 读取配置
	ticker := time.NewTicker(time.Second * 5)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			if getAddress() { // 调用函数并检查返回值
				log.Println("执行成功，退出")
				return // 如果返回true，退出程序
			}
		}
	}
}

func getAddress() bool {
	token, err := loginCpolar()
	if err != nil {
		log.Println(err)
		return false
	}

	tunnels, err := getTunnels(token)
	if err != nil {
		log.Println(err)
		return false
	}

	// 发送钉钉消息
	sendDingTalkMessage(tunnels)
	return true
}

func sendDingTalkMessage(tunnels []string) {
	// 创建一个钉钉客户端
	cli := dingtalk.InitDingTalkWithSecret(config.DingTalk.Token, config.DingTalk.Secret)
	for _, item := range tunnels {
		err := cli.SendTextMessage(item)
		if err != nil {
			log.Println("发送钉钉消息失败：", err)
		} else {
			log.Println("发送钉钉消息成功：", item)
		}
	}
}

func getTunnels(token string) (tunnels []string, err error) {
	// 使用 token 发起 GET 请求
	req, err := http.NewRequest("GET", config.Cpolar.Host+"/api/v1/tunnels", nil)
	if err != nil {
		return nil, errors.Wrap(err, "创建隧道 GET 请求失败")
	}
	req.Header.Set("Authorization", "Bearer "+token)

	// 发起 GET 请求
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, errors.Wrap(err, "发送隧道 GET 请求失败")
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, errors.Wrap(err, "获取隧道信息失败，状态码："+fmt.Sprint(resp.StatusCode))
	}
	log.Println("获取隧道信息成功。")

	// 读取 GET 响应内容
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Wrap(err, "读取隧道响应内容失败")
	}

	// 解析响应内容
	var tunnelsResponse TunnelsRes
	err = json.Unmarshal(body, &tunnelsResponse)
	if err != nil {
		return nil, errors.Wrap(err, "解析隧道响应失败")
	}

	// 检查是否成功
	if tunnelsResponse.Code != 20000 {
		return nil, errors.New("获取隧道信息失败，返回码：" + fmt.Sprint(tunnelsResponse.Code))
	}
	for _, item := range tunnelsResponse.Data.Items {
		tunnels = append(tunnels, item.PublicUrl)
	}
	return tunnels, nil
}

func loginCpolar() (token string, err error) {
	// 登录信息
	loginData := map[string]string{
		"email":    config.Cpolar.User,
		"password": config.Cpolar.Pass,
	}

	// 将登录数据序列化为 JSON
	jsonData, err := json.Marshal(loginData)
	if err != nil {
		return "", errors.Wrap(err, "登录数据序列化失败")
	}

	// 发起 POST 请求
	resp, err := http.Post(config.Cpolar.Host+"/api/v1/user/login", "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		return "", errors.Wrap(err, "发送登录 POST 请求失败")
	}
	defer resp.Body.Close()

	// 读取响应内容
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", errors.Wrap(err, "读取登录响应内容失败")
	}

	// 解析响应内容
	var loginResponse LoginRes
	err = json.Unmarshal(body, &loginResponse)
	if err != nil {
		return "", errors.Wrap(err, "解析登录响应失败")
	}

	// 检查登录是否成功
	if loginResponse.Code != 20000 {
		return "", errors.New("登录失败，返回码：" + fmt.Sprint(loginResponse.Code))
	}

	// 获取 token
	log.Println("登录成功。")
	return loginResponse.Data.Token, nil
}

func ReadConfig() {
	// 读取 YAML 文件内容
	data, err := os.ReadFile("config.yml")
	if err != nil {
		fmt.Printf("读取文件失败: %v\n", err)
		return
	}

	// 解析 YAML 数据到结构体
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		fmt.Printf("解析 YAML 文件失败: %v\n", err)
		return
	}
}

type Config struct {
	Cpolar   Cpolar
	DingTalk DingTalk
}

type Cpolar struct {
	Host string
	User string
	Pass string
}

type DingTalk struct {
	Token  string
	Secret string
}

type LoginRes struct {
	Data struct {
		Token string `json:"token"`
	} `json:"data"`
	Code int `json:"code"`
}

type TunnelsRes struct {
	Data struct {
		Items []struct {
			PublicUrl string `json:"public_url"`
		} `json:"items"`
	} `json:"data"`
	Code    int    `json:"code"`
	Message string `json:"message"`
}
